"""
解码求资源与工期
"""
import sys

import numpy as np


def serialGenerationScheme(actStr, modeStr, actList, resourceAvail):
    '''
    串行调度生成机制，传入所有活动，资源限量，优先序列
    :param actList:
    :param resourceAvail:
    :param actStr:
    :return:
    '''
    actStrToUse = actStr.copy()
    # print(actStrToUse)
    ps = [1]  # 局部调度计划初始化
    actStrToUse.remove(1)
    en = actList[1].successors.copy()  # 合格活动初始化
    actList[1].es = 0  # 活动1的最早开始时间设为0
    # print(modeStr)
    # print("*********************************")
    # print(actList[1].modes[modeStr[0]][0])
    # sys.exit()
    actList[1].ef = actList[1].es + actList[1].modes[modeStr[0]][0]
    # for i in range(1, 13):
    #     print(actList[i].modes)
    maxcount = 0
    for stage in range(1, len(actStr)):
        # maxcount += 1
        # print("当前阶段：", stage)
        selectTaskID = -1
        # print("en ", en)
        # 选出合格活动集合En中优先级最高的活动作为这一阶段要安排的活动
        for taskID in actStrToUse:
            # print("actStrToUse", actStrToUse)
            # print("check taskID ", taskID)
            if taskID in en:
                selectTaskID = taskID
                # print("select ID ", selectTaskID)
                break
        # 检查要安排的活动所有紧前活动的结束时间，最大的作为当前活动的理论最早开始时间
        earliestStartTime = 0
        for preTaskID in actList[selectTaskID].preList:
            # print(actList[preTaskID].ef)
            # print(earliestStartTime)
            if actList[preTaskID].ef > earliestStartTime:
                earliestStartTime = actList[preTaskID].ef
        # print("选中活动 ", selectTaskID)
        # print("理论最早开始时间： ", earliestStartTime)
        startTime = earliestStartTime
        # 检查满足资源限量约束的时间点作为活动最早开始时间，即在这一时刻同时满足活动逻辑约束和资源限量约束
        # 计算t时刻正在进行的活动的资源占用总量,当当前时刻大于活动开始时间小于等于活动结束时间时，说明活动在当前时刻占用资源
        # print(t)
        # print(modeStr[2])
        # print(actList[selectTaskID].modes)
        # print(actList[selectTaskID].modes[modeStr[selectTaskID-1]][0])
        modeIndex = actStr.index(selectTaskID)
        # print("modeIndex:", modeIndex)
        # sys.exit(123)
        maxcount += 1
        t = startTime + 1
        # print("startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0]:", startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0])
        while t <= startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0]:  # 当前时间T小于开始时间+工期
            # sys.exit(111)
            # print("------------------------------------------------------------")
            # print("------------------------------------------------------------")
            # print("------------------------------------------------------------")
            # print("------------------------------------------------------------")

            # print("时刻", t)
            # print("startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0]:", startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0])
            resourceSum = np.zeros(len(resourceAvail))
            # print("startTime值： ", startTime, "当前t值： ", t)
            for taskIDA in ps:
                # print("taskIDA:", taskIDA)
                # print("actList[taskIDA].es + 1:", actList[taskIDA].es + 1)
                modeIndex = actStr.index(taskIDA)
                # print(actList[taskIDA].es + actList[taskIDA].modes[modeStr[modeIndex]][0])
                # sys.exit("sddfsdf")

                if actList[taskIDA].es + 1 <= t <= actList[taskIDA].es + actList[taskIDA].modes[modeStr[modeIndex]][0]:
                    modeIndex = actStr.index(taskIDA)
                    # print("加上正在进行的活动 ", taskIDA, " 需要资源量为 ", actList[taskIDA].modes[modeStr[modeIndex]][0])
                    resourceSum = resourceSum + actList[taskIDA].modes[modeStr[modeIndex]][1:]
            # print("在时刻 ", t, " 加上待安排活动资源需求之前的资源消耗总和为 ", resourceSum)
            # 加上当前阶段需要安排的活动的资源占用，对比总资源限量是否超过
            modeIndex = actStr.index(selectTaskID)
            resourceSum = resourceSum + actList[selectTaskID].modes[modeStr[modeIndex]][1:]
            # print("在时刻 ", t, " 加上待安排活动资源需求之后的资源消耗总和为 ", resourceSum, " 资源限量为 ", resourceAvail)
            # 若超出资源限量，则向后推一个单位时间
            # print(type(resourceSum))
            # print(type(resourceAvail))
            if (resourceSum > resourceAvail).any():
                startTime += 1
                t = startTime + 1
            else:  # 没有超过
                t += 1
            # if t >= 60:  # 死循环限制测试
            #     sys.exit(999)
        # 若符合资源限量则将当前活动开始时间安排在这一时刻
        actList[selectTaskID].es = startTime
        modeIndex = actStr.index(selectTaskID)
        actList[selectTaskID].ef = startTime + actList[selectTaskID].modes[modeStr[modeIndex]][0]
        actStrToUse.remove(selectTaskID)  # 更新优先序列
        # 更新合格活动集合en，和局部调度计划ps
        en.remove(selectTaskID)
        ps.append(selectTaskID)
        # print("*********************************")
        # print("*******")
        # print("ps:", ps)
        for taskToAdd in actList[selectTaskID].successors:
            if taskToAdd in actList.keys():
                if set(actList[taskToAdd].preList) <= set(ps):
                    en.append(taskToAdd)

        # print("活动", selectTaskID, "已安排")
        # print("更新后的局部调度计划： ", ps)
        # print("更新后的合格活动集合：", en)
    duration = [actList[key].ef for key in actList.keys()]
    # print(max(duration))
    # sys.exit()
    return max(duration)


def costProcess(actStr, modeStr, actList):
    cost = 0
    for i in range(0, len(actStr)):
        cost += actList[actStr[i]].cost[modeStr[i]]
    return cost


def costImprove(result, paretoRate):
    # num = np.floor(result * paretoRate)
    return result


def CPMBasedHeuristic(actList, resources, modeNum):
    """
    CPM最早开工时间
    :param actList:
    :param resources:
    :return:
    """
    # 首先正向计算寻找最早开始时间和最早结束时间
    projectFinishT = 0
    actList[1].es = actList[1].ef = 0
    actList[1].visited = True
    for taskID in actList.keys():
        if taskID != 1 and all([actList[preTask].visited for preTask in actList[taskID].preList]) and actList[
            taskID].visited == False:
            actList[taskID].es = max([actList[preTask].ef for preTask in actList[taskID].preList])
            if taskID != len(actList):
                actList[taskID].ef = actList[taskID].es + max(
                    actList[taskID].modes[i][0] for i in range(1, modeNum + 1))
            else:
                actList[taskID].ef = actList[taskID].es + 0
            actList[taskID].visited = True
            if actList[taskID].ef > projectFinishT:
                projectFinishT = actList[taskID].ef
    # 反向计算最晚结束时间和最晚开始时间
    actList[len(actList)].lf = actList[len(actList)].ls = projectFinishT
    actList[len(actList)].visited = False
    for taskID in list(actList.keys())[::-1]:
        if taskID != len(actList) and all(
                [actList[sucTask].visited for sucTask in actList[taskID].successors]) == False and actList[
            taskID].visited == True:
            actList[taskID].lf = min([actList[sucTask].ls for sucTask in actList[taskID].successors])
            if taskID != 1:
                actList[taskID].ls = actList[taskID].lf - max(
                    actList[taskID].modes[i][0] for i in range(1, modeNum + 1))
            else:
                actList[taskID].ls = actList[taskID].lf - 0
            actList[taskID].visited = False
    # 计算关键路线
    criticalTask = []
    for key in actList.keys():
        if actList[key].es == actList[key].ls:
            criticalTask.append(key)
    nonCT = [value for value in actList.keys() if value not in criticalTask]
    sequCPM = [1]
    criticalTask.remove(1)
    # 按照关键活动尽早安排原则将非关键活动安排进序列中
    while len(sequCPM) < len(actList):
        if set(actList[criticalTask[0]].preList) <= set(sequCPM):
            sequCPM.append(criticalTask[0])
            criticalTask.pop(0)
        else:
            for nct in nonCT:
                if set(actList[nct].preList) <= set(sequCPM):
                    sequCPM.append(nct)
                    nonCT.remove(nct)
                    break
    # 求资源上界
    maxResources = upperBound(sequCPM, actList, resources, modeNum)
    return maxResources


def upperBound(sequCPM: list, actList, resources, modeNum):
    maxResources = []  # 全部不种类资源的最大值
    maxRes = np.zeros(1)  # 单个种类资源的最大值计算（此处的数组应当失效）
    # print(maxRes.tolist())
    t = 0
    middleRes = resources.copy()
    resources = []
    resources.append(0)
    for z in range(0, len(middleRes)):
        resources[0] = middleRes[z]
        for actID in sequCPM[1:-2]:
            singleResForModes = []
            tmp = []
            for i in range(1, modeNum + 1):
                tmp.append(actList[actID].modes[i])
            # print(tmp)
            tmp = np.array(tmp)
            durIndex = 0  # 指向最大资源对应工期
            for i in range(0, len(resources)):
                # print(tmp[:, i + 1:i + 2])
                col = tmp[:, i + 1:i + 2]
                durIndex = np.argwhere(col == np.max(col))
                # print(durIndex)
                singleResForModes.append(np.max(col))  # 不仅保留最大资源需求量，同时保留工期，即资源占用时间
            startTime = t + 1
            # print(actList[actID].modes[durIndex[0][0] + 1][0])
            # print("--------++++++++++")
            t = t + actList[actID].modes[durIndex[0][0] + 1][0]
            if actList[actID].es <= t <= actList[actID].ef:
                # print(singleResForModes)
                # print(maxRes)
                maxRes += singleResForModes
                # print(maxRes)
                # print("===========")
            elif actList[actID].ef > t:
                maxRes -= singleResForModes
                # print(maxRes)
        # print(maxRes.tolist())
        maxResources.append(maxRes.tolist()[0])
    return maxResources


if __name__ == '__main__':
    CPMBasedHeuristic()
